(ns lastfm-music-recommendation.controller
  (:use [compojure.core :only (defroutes GET POST)]
        [ring.util.response])
  (:require [compojure.route :as route]
            [lastfm-music-recommendation.view.view :as view]
            [lastfm-music-recommendation.db.datomic :as datomic]
            [lastfm-music-recommendation.music :as music]
            [lastfm-music-recommendation.crawler :as crawler]
            [lastfm_music_recommendation.search.search :as search]
            [lastfm-music-recommendation.util :as util]
            [lastfm-music-recommendation.search.nnet :as nnet]))

; Holds song ratings and search results for logged-in users
(def lgd-users-data (atom {}))

(defn index 
  "Login page"
  [message]
  (view/login-view message))

(defn register
  "User registration page"
  [message]
  (view/register-view message))

(defn home-page
  "Home page"
  [session]
  (if (empty? session)
    (index "You have to be logged in to view that page!") 
    (let [rated-no (count (:scores (@lgd-users-data (:user session))))
          song (music/random-song)]
      (view/home-view session rated-no song))))

(defn result-page
  "Shows recommended songs based on user ratings"
  [session]
  (if (empty? session)
    (index "You have to be logged in to view that page!") 
    (let [scores (get-in @lgd-users-data [(:user session) :scores])
          rated-no (count scores)
          result (doall (music/get-recommendation scores))]
      (view/result-view session rated-no result))))

(defn saved-songs-page
  "Shows previously saved songs"
  [session]
  (if (empty? session)
    (index "You have to be logged in to view that page!") 
    (let [rated-no (count (:scores (@lgd-users-data (:user session))))
          saved (datomic/get-saved (:user session))]
      (view/saved-songs-view session rated-no saved))))

;TODO: caching - get results from lgd-user-data if exist!?
(defn search-result
  [params session]
  "Search result page"
  (if (empty? session)
    (index "You have to be logged in to view that page!") 
    (let [rated-no (count (:scores (@lgd-users-data (:user session))))
          result (doall (search/search (:query params)))
          last (+ 1 (quot (count result) 10))
          page (read-string (:page params))
          res (take 10 (drop (* 10 (- page 1)) result))]
      (swap! lgd-users-data assoc-in [(:user session) (:query params)] result)
      (view/search-result-view session rated-no (:query params) {:result res :page page :last last}))))

(defn add-user
  "User registration function"
  [params]
  (if (empty? params)
    (register "")
    (if (or (= "" (:username params)) (= "" (:password params)) (= "" (:pass2 params)))
      (register "You have to fill in all the fields in order to register!")
      (if-not (= (:password params) (:pass2 params))
        (register "Passwords do not match!")  
          (if (datomic/register-user (:username params) (:password params))
            (index "You are seccessfuly registred!")
            (register "Error! Username already exists!"))))))

(defn log-in 
  "Log in function"
  [params]
  (if (empty? params) 
    (index "")    
    (if (= "" (:username params) (:password params))
      (index "Empty username and password!")    
      (if (datomic/login (:username params) (:password params))
          (assoc (redirect "/home") :session {:user (:username params)})
        (index "Invalid username and/or password!")))))

(defn log-out
  "Log out function"
  [session]
  (swap! lgd-users-data dissoc (:user session)) 
  (assoc (redirect "/") :session {}))

(defn rate-song
  "Adds new song rating"
  [params session]
  (swap! lgd-users-data assoc-in [(:user session) :scores [(:artist params) (:song params)]] (:score params)))

(defn save
  "Saves recommended songs into db"
  [session]
  (let [scores (get-in @lgd-users-data [(:user session) :scores])] 
    (datomic/save (:user session) (vec (music/get-recommendation scores)))
    (saved-songs-page session)))

(defn train
  "Trains neural network when song is selected from search results"
  [user params]
  (if-let [result (get-in @lgd-users-data [user (:query params)])]
    (nnet/train-query (:query params) 
                      (util/get-songs result)
                      (read-string (:selected params)))))

(defroutes routes
  (GET  "/" [] (index ""))
  (GET "/register" [] (register ""))
  (POST "/register" {params :params} (add-user params))
  (POST "/login" {params :params}  (log-in params))
  (GET "/home" {session :session} (home-page session))
  (GET "/rate" {params :params session :session} (rate-song params session))
  (GET "/result" {session :session} (result-page session))
  (GET "/save" {session :session} (save session))
  (GET "/saved" {session :session} (saved-songs-page session))
  (GET "/logout" {session :session} (log-out session))
  (GET "/search" {params :params session :session} (search-result params session))
  (GET "/train" {session :session params :params} (train (:user session) params))
  (POST "/crawl" [] (crawler/crawl))
  (POST "/crawlstop" [] (crawler/stop-crawling)))
 